(*
 * Named timers.  Each timer is associated with a string name.
 * Operations are starting, stopping (or pausing), and checking
 * the time elapsed on the named timer.  A single timer may be
 * started and stopped multiple times, and the elapsed time will
 * always be the total time spent running.
 *)


(* Hashes of start times and elapsed times *)
let start_time_hash = Hashtbl.create 100 
let elapsed_time_hash = Hashtbl.create 100 

(* Helper functions, for internal use only. *)
let _get_elapsed s =
  try Hashtbl.find elapsed_time_hash s
  with Not_found -> 0.0

let _get_start s =
  try Hashtbl.find start_time_hash s
  with Not_found -> Sys.time() 

(* Start a timer with the given name.  Does nothing if timer has
   already been started. *)
let start (s:string) =
  if not (Hashtbl.mem start_time_hash s) then
    Hashtbl.add start_time_hash s (Sys.time())

(* Returns time passed since the timer was most recently started or
 * delta was last called for this timer.  Useful for timing sequential
 * pieces. *)
let delta (s:string) =
  let now = Sys.time() in
  let old_elapsed = _get_elapsed s in 
  let new_elapsed = now -. _get_start s in 
  Hashtbl.replace elapsed_time_hash s (new_elapsed +. old_elapsed);
  Hashtbl.replace start_time_hash s now;
  new_elapsed

(* Amount of time elapsed on the named timer.  Counts time since
 * timer was started, excluding time while it was stopped. *)
let elapsed (s:string) =
  _get_elapsed s +. (Sys.time() -. _get_start s)

(* Stop named timer.  This pauses it until restarted with start. *)
let stop (s:string) =
  Hashtbl.replace elapsed_time_hash s (elapsed s);
  Hashtbl.remove start_time_hash s

(* Stop named timer and set its elapsed time to zero. *)
let clear (s:string) =
  Hashtbl.remove start_time_hash s;
  Hashtbl.remove elapsed_time_hash s
